package com.jinly.ryder.database.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jinly.ryder.common.error.SystemErrorType;
import com.jinly.ryder.database.dao.SqlTaskItemMapper;
import com.jinly.ryder.database.dao.SqlTaskMapper;
import com.jinly.ryder.database.dao.entity.SqlTaskDO;
import com.jinly.ryder.database.dao.entity.SqlTaskItemDO;
import com.jinly.ryder.database.service.DatabaseExecService;
import com.jinly.ryder.database.service.SqlTaskService;
import com.jinly.ryder.database.service.mapping.SqlTaskItemMapping;
import com.jinly.ryder.database.service.mapping.SqlTaskMapping;
import com.jinly.ryder.database.service.model.SqlTask;
import com.jinly.ryder.database.service.model.SqlTaskItem;
import com.jinly.ryder.database.service.model.SqlTaskState;
import com.jinly.ryder.database.service.model.SqlType;
import com.jinly.ryder.flow.service.model.ApplyStatus;
import com.jinly.ryder.flow.service.model.FlowApply;
import com.jinly.ryder.flow.service.model.FlowAudit;
import com.jinly.ryder.manage.service.model.DatabaseInstance;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.alter.Alter;
import net.sf.jsqlparser.statement.create.index.CreateIndex;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class SqlTaskServiceImpl implements SqlTaskService {
    private final SqlTaskMapper sqlTaskMapper;
    private final SqlTaskItemMapper sqlTaskItemMapper;
    private final SqlTaskMapping sqlTaskMapping;
    private final SqlTaskItemMapping sqlTaskItemMapping;
    private final DatabaseExecService databaseExecService;


    @Override
    public Page<SqlTask> sqlTaskPage(Page page, SqlTask query) {
        Wrapper<SqlTaskDO> queryWrapper = new QueryWrapper<SqlTaskDO>().lambda()
                .eq(query.getApplyId() != null, SqlTaskDO::getApplyId, query.getApplyId())
                .eq(query.getApplyUserId() != null, SqlTaskDO::getApplyUserId, query.getApplyUserId())
                .eq(query.getDatabaseName() != null, SqlTaskDO::getDatabaseName, query.getDatabaseName())
                .eq(query.getApplyStatus() != null, SqlTaskDO::getApplyStatus, query.getApplyStatus())
                .eq(query.getState() != null, SqlTaskDO::getState, query.getState())
                .eq(query.getAppName() != null, SqlTaskDO::getAppName, query.getAppName())
                .orderByDesc(SqlTaskDO::getId);

        return sqlTaskMapping.toModelPage(sqlTaskMapper.selectPage(page, queryWrapper));
    }


    @Override
    public void sqlTaskApply(FlowApply flowApply, FlowAudit flowAudit) {
        JSONObject formData = flowApply.getApplyFormData();
        switch (flowApply.getApplyStatus()) {
            case SUBMIT:
                SqlTask sqlTask = formData.toJavaObject(SqlTask.class);
                sqlTask.setApplyId(flowApply.getId());
                sqlTask.setApplyUserId(flowApply.getApplyUserId());
                sqlTask.setAppName(flowApply.getAppName());
                sqlTask.setRemark(flowApply.getRemark());
                sqlTask.setItemList(parseSqlToItem(sqlTask.getSqlScript()));
                sqlTask.setApplyStatus(flowApply.getApplyStatus());
                submitSqlTask(sqlTask);
                break;
            case CANCEL:
            case REJECT:
            case APPROVE:
                updateTaskApplyStatus(flowApply.getId(), flowApply.getApplyStatus());
        }
    }

    private void updateTaskApplyStatus(Long applyId, ApplyStatus applyStatus) {
        SqlTask sqlTask = getSqlTaskByApplyId(applyId);
        if (sqlTask != null) {
            sqlTask.setApplyStatus(applyStatus);
            sqlTaskMapper.updateById(sqlTaskMapping.toEntity(sqlTask));
        }
    }

    @Override
    public SqlTask getSqlTaskByApplyId(Long applyId) {
        Wrapper<SqlTaskDO> queryWrapper = new QueryWrapper<SqlTaskDO>().lambda()
                .eq(SqlTaskDO::getApplyId, applyId)
                .last("limit 1");
        return sqlTaskMapping.toModel(sqlTaskMapper.selectOne(queryWrapper));
    }


    private void submitSqlTask(SqlTask sqlTask) {
        Assert.isTrue(sqlTask.getAppName() != null, "   应用不能为空");
        Assert.isTrue(sqlTask.getDatabaseId() != null, "数据库不能为空");
        Assert.isTrue(sqlTask.getItemList() != null && sqlTask.getItemList().size() > 0, "SQL脚本不能为空");
        if (sqlTask.getItemList().size() > 100) {
            SystemErrorType.BAD_REQUEST.throwError("SQL语句过多，最多100条");
        }
        sqlTask.setState(SqlTaskState.WAITING);
        for (SqlTaskItem item : sqlTask.getItemList()) {
            SqlTaskItem parseItem = parseSql(item.getSqlScript());
            if (parseItem.getState() == SqlTaskState.FAIL) {
                SystemErrorType.BAD_REQUEST.throwError(parseItem.getResultMsg());
            }
            item.setType(parseItem.getType());
            item.setTableName(parseItem.getTableName());
            item.setState(SqlTaskState.WAITING);
        }
        sqlTask.setState(SqlTaskState.WAITING);
        SqlTaskDO sqlTaskDO = sqlTaskMapping.toEntity(sqlTask);
        sqlTaskMapper.insert(sqlTaskDO);
        sqlTask.getItemList().forEach(t -> {
            t.setTaskId(sqlTaskDO.getId());
            sqlTaskItemMapper.insert(sqlTaskItemMapping.toEntity(t));
        });
    }

    @Override
    public void execSqlTask(SqlTask sqlTask, DatabaseInstance instance) {
        if (sqlTask.getApplyStatus() != ApplyStatus.APPROVE) {
            return;
        }
        List<SqlTaskItem> itemList = queryItemList(sqlTask.getId());
        sqlTask.setState(SqlTaskState.SUCCESS);
        for (SqlTaskItem item : itemList) {
            execSqlTaskItem(instance, item);
            if (item.getState() == SqlTaskState.FAIL) {
                sqlTask.setState(SqlTaskState.FAIL);
                break;
            }
        }
        sqlTaskMapper.updateById(sqlTaskMapping.toEntity(sqlTask));
    }

    private void execSqlTaskItem(DatabaseInstance instance, SqlTaskItem item) {
        try {
            databaseExecService.execSql(instance, item.getSqlScript());
            item.setState(SqlTaskState.SUCCESS);
        } catch (Exception e) {
            item.setState(SqlTaskState.FAIL);
            item.setResultMsg(e.getMessage());
        } finally {
            sqlTaskItemMapper.updateById(sqlTaskItemMapping.toEntity(item));
        }
    }

    @Override
    public SqlTask getSqlTask(Long taskId, boolean withItem) {
        SqlTask sqlTask = sqlTaskMapping.toModel(sqlTaskMapper.selectById(taskId));
        if (sqlTask != null && withItem) {
            sqlTask.setItemList(queryItemList(taskId));
        }
        return sqlTask;
    }

    @Override
    public List<SqlTaskItem> queryItemList(Long taskId) {
        Wrapper<SqlTaskItemDO> queryWrapper = new QueryWrapper<SqlTaskItemDO>().lambda()
                .eq(SqlTaskItemDO::getTaskId, taskId);
        return sqlTaskItemMapping.toModelList(sqlTaskItemMapper.selectList(queryWrapper));
    }

    /**
     * 解析sql
     */
    @Override
    public List<SqlTaskItem> parseSqlToItem(String sqlText) {
        List<SqlTaskItem> itemList = new ArrayList<>();
        String[] sqlArray = sqlText.split(";");
        if (sqlArray.length > 100) {
            SystemErrorType.BAD_REQUEST.throwError("SQL语句过多，最多100条");
        }
        for (String sql : sqlArray) {
            sql = sql.trim();
            if (sql.replaceAll("\\n", "").length() > 0) {
                SqlTaskItem item = parseSql(sql);
                itemList.add(item);
            }
        }
        return itemList;
    }

    private SqlTaskItem parseSql(String sql) {
        SqlTaskItem item = new SqlTaskItem();
        item.setSqlScript(sql);
        try {
            Statement statement = CCJSqlParserUtil.parse(sql);
            setSqlType(statement, item);
            if (item.getType() == null) {
                item.setState(SqlTaskState.FAIL);
                item.setResultMsg("不支持的SQL类型,请联系管理员");
                log.error("不支持的SQL类型:{}", statement.getClass());
            } else {
                item.setState(SqlTaskState.SUCCESS);
            }
        } catch (Exception e) {
            log.error("SQL解析失败:{}:{}", sql, e.getMessage());
            item.setState(SqlTaskState.FAIL);
            item.setResultMsg("SQL解析失败，请检查SQL语法");
        }
        return item;
    }

    private void setSqlType(Statement statement, SqlTaskItem item) {
        if (statement instanceof Insert) {
            item.setType(SqlType.INSERT);
            item.setTableName(((Insert) statement).getTable().getName());
        } else if (statement instanceof Update) {
            item.setType(SqlType.UPDATE);
            item.setTableName(((Update) statement).getTable().getName());
        } else if (statement instanceof Delete) {
            item.setType(SqlType.DELETE);
            item.setTableName(((Delete) statement).getTable().getName());
        } else if (statement instanceof CreateTable) {
            item.setType(SqlType.CRATE_TABLE);
            CreateTable createTable = (CreateTable) statement;
            item.setTableName(createTable.getTable().getName());
        } else if (statement instanceof CreateIndex) {
            item.setType(SqlType.CRATE_INDEX);
            item.setTableName(((CreateIndex) statement).getTable().getName());
        } else if (statement instanceof Alter) {
            item.setType(SqlType.Alter_TABLE);
            item.setTableName(((Alter) statement).getTable().getName());
        }
    }

}
